﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NewUIConceptTest
{
    public class ConsoleCanvas : ICanvas
    {
        #region ICanvas Members

        public Int32 BackgroundColor
        {
            get { return ConsoleColorConverter.ConvertFrom(Console.BackgroundColor); }
            set { Console.BackgroundColor = ConsoleColorConverter.ConvertTo(value); }
        }

        public Int32 Width
        {
            get { return Console.WindowWidth; }
            set { Console.WindowWidth = value; }
        }

        public Int32 Height
        {
            get { return Console.WindowHeight; }
            set { Console.WindowHeight = value; }
        }

        public void DrawSpriteAt(ISprite sprite, Int32 x, Int32 y)
        {
            if (sprite == null)
            {
                return;
            }

            Console.SetCursorPosition(x, y);
            sprite.Draw(this);
        }

        #endregion
        static class ConsoleColorConverter
        {
            private static Dictionary<Int32, ConsoleColor> _consoleColorLookup
                = new Dictionary<Int32, ConsoleColor>();
            private static Dictionary<ConsoleColor, Int32> _intColorLookup
                = new Dictionary<ConsoleColor, Int32>();

            static ConsoleColorConverter()
            {
                buildColorLookupTables();
            }

            public static ConsoleColor ConvertTo(Int32 value)
            {
                // An Int32 has 32 bits, or 4 bytes. Colors are encoded
                // by using 1 byte for each of the following colors:
                // red, green and blue. The first byte is ignored.
                Byte[] colorBytes = BitConverter.GetBytes(value);

                // ignore the first byte
                colorBytes[0] = 0;

                // We need to round the byte values, since the only valid values
                // are 0xff (255), 0x7f (127) and 0x00 (0).
                for (Int32 i = 1; i < colorBytes.Length; i++)
                {
                    Byte current = colorBytes[i];

                    if (current >= 0x7f)
                    {
                        // compute the midpoint between 127 and 255.
                        const Byte midpoint = (0xff + 0x7f) / 2;

                        // which value is greater, the midpoint or the current byte?
                        if (Math.Max(midpoint, current) >= midpoint)
                        {
                            // the current value is greater or equal, round up
                            current = 0xff;
                        }
                        else
                        {
                            // the midpoint is greater, round down.
                            current = 0x7f;
                        }
                    }
                    else
                    {
                        // compute the midpoint between 0 and 126.
                        const Byte midpoint = 0x7e / 2;

                        // which value is greater, the midpoint or the current byte?
                        if (Math.Max(midpoint, current) >= midpoint)
                        {
                            // the current value is greater or equal, round up
                            current = 0x7f;
                        }
                        else
                        {
                            // the midpoint is greater, round down.
                            current = 0;
                        }
                    }

                    colorBytes[i] = current;
                }

                // now we have bytes which are the correct values for 
                // the console colors

                //return getConsoleColorInefficient(colorBytes);
                return getConsoleColorEfficient(colorBytes);
            }

            public static Int32 ConvertFrom(ConsoleColor color)
            {
                return _intColorLookup[color];
            }

            private static void buildColorLookupTables()
            {
                // Int32 -> ConsoleColor table

                // dark colors
                _consoleColorLookup[0x00000000] = ConsoleColor.Black;
                _consoleColorLookup[0x0000007f] = ConsoleColor.DarkBlue;
                _consoleColorLookup[0x00007f00] = ConsoleColor.DarkGreen;
                _consoleColorLookup[0x00007f7f] = ConsoleColor.DarkCyan;
                _consoleColorLookup[0x007f0000] = ConsoleColor.DarkRed;
                _consoleColorLookup[0x007f007f] = ConsoleColor.DarkMagenta;
                _consoleColorLookup[0x007f7f00] = ConsoleColor.DarkYellow;
                _consoleColorLookup[0x007f7f7f] = ConsoleColor.Gray;

                // bright colors
                _consoleColorLookup[0x000000ff] = ConsoleColor.Blue;
                _consoleColorLookup[0x0000ff00] = ConsoleColor.Green;
                _consoleColorLookup[0x00ff0000] = ConsoleColor.Red;
                _consoleColorLookup[0x0000ffff] = ConsoleColor.Cyan;
                _consoleColorLookup[0x00ff00ff] = ConsoleColor.Magenta;
                _consoleColorLookup[0x00ffff00] = ConsoleColor.Yellow;
                _consoleColorLookup[0x00ffffff] = ConsoleColor.White;

                // ConsoleColor -> Int32 table

                foreach (KeyValuePair<Int32, ConsoleColor> pair in _consoleColorLookup)
                {
                    _intColorLookup[pair.Value] = pair.Key;
                }
            }

            private static ConsoleColor getConsoleColorEfficient(Byte[] colorBytes)
            {
                Int32 key = BitConverter.ToInt32(colorBytes, 0);
                return _consoleColorLookup[key];
            }

            private static ConsoleColor getConsoleColorInefficient(Byte[] colorBytes)
            {
                switch (colorBytes[1])
                {
                    case 0x00:
                        switch (colorBytes[2])
                        {
                            case 0x00:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.Black; // 0x00, 0x00, 0x00
                                    case 0x7f:
                                        return ConsoleColor.DarkBlue;  // 0x00, 0x00, 0x7f
                                    case 0xff:
                                        return ConsoleColor.Blue;  // 0x00, 0x00, 0xff
                                }
                                break;
                            case 0x7f:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.DarkGreen; // 0x00, 0x7f, 0x00
                                    case 0x7f:
                                        return ConsoleColor.DarkCyan; // 0x00, 0x7f, 0x7f
                                    case 0xff:
                                        throw new NotSupportedException("Color not supported.");
                                }
                                break;
                            case 0xff:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.Green; // 0x00, 0xff, 0x00
                                    case 0x7f:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0xff:
                                        return ConsoleColor.Cyan; // 0x00, 0xff, 0xff
                                }
                                break;
                        }
                        break;
                    case 0x7f:
                        switch (colorBytes[2])
                        {
                            case 0x00:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.DarkRed; // 0x7f, 0x00, 0x00
                                    case 0x7f:
                                        return ConsoleColor.DarkMagenta;  // 0x7f, 0x00, 0x7f
                                    case 0xff:
                                        throw new NotSupportedException("Color not supported.");
                                }
                                break;
                            case 0x7f:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.DarkYellow; // 0x7f, 0x7f, 0x00
                                    case 0x7f:
                                        return ConsoleColor.DarkGray; // 0x7f, 0x7f, 0x7f
                                    case 0xff:
                                        throw new NotSupportedException("Color not supported.");
                                }
                                break;
                            case 0xff:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0x7f:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0xff:
                                        throw new NotSupportedException("Color not supported.");
                                }
                                break;
                        }
                        break;
                    case 0xff:
                        switch (colorBytes[2])
                        {
                            case 0x00:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.Red; // 0xff, 0x00, 0x00
                                    case 0x7f:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0xff:
                                        return ConsoleColor.Magenta;  // 0xff, 0x00, 0xff
                                }
                                break;
                            case 0x7f:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0x7f:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0xff:
                                        throw new NotSupportedException("Color not supported.");
                                }
                                break;
                            case 0xff:
                                switch (colorBytes[3])
                                {
                                    case 0x00:
                                        return ConsoleColor.Yellow; // 0xff, 0xff, 0x00
                                    case 0x7f:
                                        throw new NotSupportedException("Color not supported.");
                                    case 0xff:
                                        return ConsoleColor.White; // 0xff, 0xff, 0xff
                                }
                                break;
                        }
                        break;
                }

                throw new NotSupportedException("Unknown color.");
            }
        }


        public void Write(Char character)
        {
            Console.Write(character);
        }
    }
}
